/*
 * This file is part of the Sx Framework Library.
 * 
 * Copyright (C) 2013 University of Colorado Denver
 * <min.choi@ucdenver.edu> <shane.transue@ucdenver.edu>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy 
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
 * copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in 
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 * DEALINGS IN THE SOFTWARE.
 */
#include <sxOBJ_VegaMassSpringMeshStorage.h>

/* Simulation Object Created by this Storage Type */
#include <sxMassSpringMesh.h>

/* OBJ File Loading Includes */
#include <sxOBJ_File.h>
#include <sxOBJ_FileDefinition.h>
#include <sxOBJ_Mesh.h>
#include <sxOBJ_Face.h>

/* Mass-Spring Physical Representation Includes */
#include <sxVega_MassSpringSystem.h>

Sx::Vega::Storage::OBJ_VegaMassSpringMeshStorage::OBJ_VegaMassSpringMeshStorage() {
	this->surfaceDensity = 1000.0;
	this->tensileStiffness = 2400.0;
	this->shearStiffness = 2400.0;
	this->bendStiffness = 2400.0;
	this->damping = 0.001;
}

Sx::Vega::Storage::OBJ_VegaMassSpringMeshStorage::~OBJ_VegaMassSpringMeshStorage() {

}

std::shared_ptr<Sx::Simulation::Objects::MassSpringMesh> Sx::Vega::Storage::OBJ_VegaMassSpringMeshStorage::load(const std::shared_ptr<Simulation::Objects::Rendering::RendererImplementation>& renderer, const String& filename) const {
	if ( filename.length() == 0 ) {
		std::cerr << "[OBJ_MassSpringMeshStorage:load] Error: Cannot create OBJ mass spring mesh from filename with length 0." << std::endl;
		std::cerr << "  OBJ Mass Spring Mesh not loaded, returning nullptr." << std::endl;
		return nullptr;
	}

	if ( renderer == nullptr ) {
		std::cerr << "[OBJ_MassSpringMeshStorage:load] Error: Renderer cannot be a nullptr." << std::endl;
		std::cerr << "  OBJ Mass Spring Mesh not loaded, returning nullptr." << std::endl;
		return nullptr;
	}

	//--------------------------------------------------------------------------
	// Construct the abstract and physical representations for the new mesh.
	//--------------------------------------------------------------------------
	std::shared_ptr<Graphics::Objects::Mesh> mesh = std::make_shared<Graphics::Objects::Mesh>();
	std::shared_ptr<Physics::VegaMassSpringSystem> vegaMassSpringSystem = std::make_shared<Physics::VegaMassSpringSystem>();

	//--------------------------------------------------------------------------
	// Load the OBJ file representation.
	//--------------------------------------------------------------------------
	Graphics::File::OBJ_File objFile;
	if ( !objFile.load(filename) ) return false;

	//--------------------------------------------------------------------------
	// Load the mesh definition (faces) from the OBJ file into the mesh.
	//--------------------------------------------------------------------------
	const Graphics::File::OBJ_FileDefinition* objDef = objFile.getDefinition();
	if ( objDef->meshes.size() == 0 ) {
		std::cerr << "[OBJ_MassSpringMeshStorage:load] Error: Loaded OBJ file: " << filename << " does not contain any meshes." << std::endl;
		std::cerr << "  Aborting OBJ MassSpring mesh construction." << std::endl;
		return nullptr;
	}

	//--------------------------------------------------------------------------
	// Assuming that only the first mesh of the file should be used.
	//--------------------------------------------------------------------------
	Graphics::File::OBJ_Mesh* objMesh = objDef->meshes[0];
	if ( objMesh == nullptr ) {
		std::cerr << "[OBJ_MassSpringMeshStorage:load] Error: OBJ file mesh is a nullptr. Check OBJ file: " << filename << std::endl;
		std::cerr << "  Aborting OBJ MassSpring mesh construction." << std::endl;
		return nullptr;
	}

	//--------------------------------------------------------------------------
	// Add all of the faces from the OBJ file to the abstract Mesh.
	//--------------------------------------------------------------------------
	for ( unsigned int i = 0; i < objMesh->faces.size(); i++ ) {
		Graphics::File::OBJ_Face& objFace = objMesh->faces[i];
		Graphics::Primitives::IndiceFace indiceFace;
		
		//----------------------------------------------------------------------
		// Add all of the indices for the current face.
		//----------------------------------------------------------------------
		for ( unsigned int x = 0; x < objFace.vertexIndices.size(); x++ ) {
			indiceFace.add(objFace.vertexIndices[x]);
		}

		mesh->addFace(indiceFace);
	}

	//--------------------------------------------------------------------------
	// Load the texture coordinates into the Mesh.
	//--------------------------------------------------------------------------
	for ( unsigned int i = 0; i < objMesh->textureCoordinates.size(); i++ )
		mesh->addCoordinate(objMesh->textureCoordinates[i]);

	//--------------------------------------------------------------------------
	// Load the vertices into the physical representation. The OBJ file usually
	// will not contain a high level of precision. This lower level of 
	// precision is converted to the internal double type.
	//--------------------------------------------------------------------------
	Eigen::Vector3d curVertex;
	for ( unsigned int i = 0; i < objMesh->vertices.size(); i++ ) {
		curVertex = objMesh->vertices[i].cast<double>();
		vegaMassSpringSystem->addNode(curVertex);
	}

	//--------------------------------------------------------------------------
	// Load the vertex normals into the physical representation.
	//--------------------------------------------------------------------------
	Eigen::Vector3d curNormal;
	for ( unsigned int i = 0; i < objMesh->normals.size(); i++ ) {
		curNormal = objMesh->normals[i].cast<double>();
		vegaMassSpringSystem->addVertexNormal(curNormal);
	}

	//--------------------------------------------------------------------------
	// Set any additional properties of the loaded mesh.
	//--------------------------------------------------------------------------
	mesh->setName(objMesh->name);

	//--------------------------------------------------------------------------
	// Initialize the internals of the Vega mass spring system.
	//--------------------------------------------------------------------------
	vegaMassSpringSystem->constructFromQuadMesh(mesh, this->surfaceDensity, this->tensileStiffness, this->shearStiffness, this->bendStiffness, this->damping);
	mesh->triangulateFaces();

	//--------------------------------------------------------------------------
	// Construct the simulation mesh. This is an object composed of a Mesh,
	// a default renderer, and a mass-spring system.
	//--------------------------------------------------------------------------
	std::shared_ptr<MassSpringMesh> massSpringMesh = std::make_shared<MassSpringMesh>();
	massSpringMesh->setArchetype(mesh, renderer, vegaMassSpringSystem);
	massSpringMesh->construct();
	return massSpringMesh;
}

bool Sx::Vega::Storage::OBJ_VegaMassSpringMeshStorage::save(const String& filename, const std::shared_ptr<MassSpringMesh>& object) const {
	return false;
}
